package com.jdzy.student.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jdzy.common.constants.CommonConstants;
import com.jdzy.common.constants.RedisConstant;
import com.jdzy.common.constants.StudentConstants;
import com.jdzy.common.exception.InsertNullException;
import com.jdzy.common.exception.UpdateException;
import com.jdzy.model.common.vos.Result;
import com.jdzy.model.student.dtos.StudentDto;
import com.jdzy.model.student.dtos.StudentLoginDto;
import com.jdzy.model.student.pojos.Student;
import com.jdzy.model.student.pojos.StudentBaseInformation;
import com.jdzy.model.student.pojos.StudentTagRelation;
import com.jdzy.model.student.pojos.Tag;
import com.jdzy.model.student.vos.CaptchaVo;
import com.jdzy.model.student.vos.StudentVo;
import com.jdzy.student.mapper.StudentBaseInformationMapper;
import com.jdzy.student.mapper.StudentMapper;
import com.jdzy.student.mapper.StudentTagRelationMapper;
import com.jdzy.student.mapper.TagMapper;
import com.jdzy.student.properties.JwtProperties;
import com.jdzy.student.service.StudentService;
import com.jdzy.utils.common.JwtUtils;
import com.jdzy.utils.common.MD5Utils;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class StudentServiceImpl  extends ServiceImpl<StudentMapper, Student> implements StudentService{

    @Autowired
    private StudentMapper studentMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private StudentBaseInformationMapper studentBaseInformationMapper;

    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private StudentTagRelationMapper studentTagRelationMapper;
    @Autowired
    private TagMapper tagMapper;

    /**
     * 根据学号stuId获取学生信息
     *
     * @param stuId
     * @return
     */
    @Override
    @Transactional
    public Result getStudentByStuId(String stuId) {
        log.info("根据学号stuId获取学生信息,参数:{}",stuId);
        //1、检查参数
        if(StringUtils.isBlank(stuId)){
            return Result.error(StudentConstants.PARAM_EMPTY);
        }
        //2、获取学生信息
        LambdaQueryWrapper<Student> studentLambdaQueryWrapper = new LambdaQueryWrapper<Student>().eq(Student::getStuId, stuId);
        Student student = studentMapper.selectOne(studentLambdaQueryWrapper);
        //判断学生是否存在
        if(student==null){
            return Result.error(StudentConstants.USER_NOT_EXIST);
        }
        //3、获取学生的基本信息
        StudentBaseInformation studentBaseInformation =
                studentBaseInformationMapper.selectOne(Wrappers.<StudentBaseInformation>lambdaQuery()
                        .eq(StudentBaseInformation::getStuId, stuId));

        // 4、获取学生的标签信息
        List<String> tags = new ArrayList<>();
        if (student != null) {
            List<StudentTagRelation> studentTagRelations = studentTagRelationMapper.selectList(Wrappers.<StudentTagRelation>lambdaQuery().eq(StudentTagRelation::getStuId, stuId));
            for (StudentTagRelation relation : studentTagRelations) {
                Tag tag = tagMapper.selectById(relation.getTagId());
                if (tag != null) {
                    tags.add(tag.getName());
                }
            }
        }

        //5、开始封装信息
        StudentVo studentVo=new StudentVo();
        BeanUtils.copyProperties(student,studentVo);
        BeanUtils.copyProperties(studentBaseInformation,studentVo);
        studentVo.setTags(tags);

        //修改时间updateTime根据student和StudentBaseInformation哪个更后面（大）就用哪个
        LocalDateTime studentUpdateTime = student.getUpdateTime();
        LocalDateTime studentBaseInformationUpdateTime = studentBaseInformation.getUpdateTime();
        if(studentUpdateTime.compareTo(studentBaseInformationUpdateTime)<0){
            studentVo.setUpdateTime(studentBaseInformationUpdateTime);
        }else{
            studentVo.setUpdateTime(studentUpdateTime);
        }

        return Result.success(studentVo);
    }

    /**
     * 注册学生信息
     *
     * @param studentDto
     * @return
     */
    @Override
    @Transactional
    public Result saveStudent(StudentDto studentDto) {
        //1、判断参数问题
        if(StringUtils.isBlank(studentDto.getPassword()) || StringUtils.isBlank(studentDto.getStuId())
                || StringUtils.isBlank(studentDto.getNationalIdentificationNumber())){
            return Result.error(CommonConstants.PARAM_REQUIRE);
        }
        synchronized ("saveStudent"+CommonConstants.LOCK_INDEX+studentDto.getStuId()){
            //2、判断是否有已存在的学号
            if(studentMapper.selectOne(Wrappers.<Student>lambdaQuery().eq(Student::getStuId, studentDto.getStuId()))!=null){
                return Result.error(StudentConstants.StUID_EXIST);
            }
            //3、判断是否有已存在的身份证号
            if(studentBaseInformationMapper.selectOne(Wrappers.<StudentBaseInformation>lambdaQuery()
                    .eq(StudentBaseInformation::getNationalIdentificationNumber,studentDto.getNationalIdentificationNumber()))!=null){
                return Result.error(StudentConstants.NationalIdentificationNumber_EXIST);
            }
            //4、进行随机获取加密盐
            String salt = UUID.randomUUID().toString().replaceAll("-", "");
            //5、进行md5加密
            String encryptPassword = MD5Utils.encodeWithSalt(studentDto.getPassword(), salt);
            studentDto.setPassword(encryptPassword);
            //6、进行封装数据
            Student student = new Student();
            BeanUtils.copyProperties(studentDto,student);
            StudentBaseInformation studentBaseInformation = new StudentBaseInformation();
            BeanUtils.copyProperties(studentDto,studentBaseInformation);
            student.setSalt(salt);
            //7、保存到数据库中
            int insertStudentNum = studentMapper.insert(student);
            if(insertStudentNum==0){
                log.error("saveStudent,新增到student表失败");
                return Result.error(CommonConstants.INSERT_FAIL);
            }

            int insertStudentBaseInformationNum = studentBaseInformationMapper.insert(studentBaseInformation);
            try {
                if(insertStudentBaseInformationNum==0){
                    //新增失败 报错 回滚事务
                    throw new InsertNullException();
                }
            }catch (InsertNullException insertNullException){
                //回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                log.error("saveStudent,新增到StudentBaseInformation表失败");
                return Result.error(CommonConstants.INSERT_FAIL);
            }

            return Result.success();
        }
    }

    /**
     * 根据学号修改学生信息
     * @param studentDto
     * @return
     */
    @Override
    @Transactional
    public Result updateStudentByStuId(StudentDto studentDto) {
        //1、参数检查
        if(StringUtils.isBlank(studentDto.getStuId())){
            return Result.error(StudentConstants.PARAM_EMPTY);
        }
        synchronized ("updateStudentByStuId"+CommonConstants.LOCK_INDEX+studentDto.getStuId()){
            //2.1、查询数据库student表中有无
            Student student = studentMapper.selectOne(Wrappers.<Student>lambdaQuery()
                    .eq(Student::getStuId, studentDto.getStuId()));
            if(student==null){
                Result.error(CommonConstants.DATASOURCE_IS_NULL);
            }
            //2.2、查询数据库StudentBaseInformation中有无
            StudentBaseInformation studentBaseInformation = studentBaseInformationMapper
                    .selectOne(Wrappers.<StudentBaseInformation>lambdaQuery()
                            .eq(StudentBaseInformation::getStuId, studentDto.getStuId()));
            if(studentBaseInformation==null){
                Result.error(CommonConstants.DATASOURCE_IS_NULL);
            }
            //2.3、数据拷贝
            BeanUtils.copyProperties(studentDto,student);
            BeanUtils.copyProperties(studentDto,studentBaseInformation);

            //3、修改数据库
            //修改student表
            LambdaUpdateWrapper<Student> studentUpdateWrapper = new LambdaUpdateWrapper<>();
            studentUpdateWrapper.set(Student::getSex,student.getSex());
            studentUpdateWrapper.eq(Student::getStuId,student.getStuId());
            int updateNum1 = studentMapper.update(student, studentUpdateWrapper);
            if(updateNum1<0){
                return Result.error(StudentConstants.UPDATE_FAIL);
            }
            //修改学生信息表
        /*
            学号  姓名  身份号 学校名称-----这些信息不应该被修改
        */
            LambdaUpdateWrapper<StudentBaseInformation> studentBaseInformationUpdateWrapper = new LambdaUpdateWrapper<>();
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getPosition,studentBaseInformation.getPosition());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getPoliticsStatus,studentBaseInformation.getPoliticsStatus());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getNation,studentBaseInformation.getNation());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getEduBackground,studentBaseInformation.getEduBackground());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getSchoolStatus,studentBaseInformation.getSchoolStatus());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getFaculty,studentBaseInformation.getFaculty());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getProfession,studentBaseInformation.getProfession());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getClassroom,studentBaseInformation.getClassroom());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getPhone,studentBaseInformation.getPhone());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getMail,studentBaseInformation.getMail());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getQq,studentBaseInformation.getQq());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getWechat,studentBaseInformation.getWechat());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getEnrollmentDate,studentBaseInformation.getEnrollmentDate());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getAnticipatedGraduationDate,studentBaseInformation.getAnticipatedGraduationDate());
            studentBaseInformationUpdateWrapper.set(StudentBaseInformation::getGraduationDate,studentBaseInformation.getGraduationDate());
            int updateNum2 = studentBaseInformationMapper.update(studentBaseInformation, studentBaseInformationUpdateWrapper);
            try {
                if(updateNum2<0){
                    throw new UpdateException();

                }
            }catch (UpdateException updateException){
                //手动回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return Result.error(StudentConstants.UPDATE_FAIL);
            }
//        //修改标签表
//            if(studentDto.getTagIds()!=null && studentDto.getTagIds().size()>0){
//                //删除该学生的所有标签
//                studentTagRelationMapper.delete(Wrappers.<StudentTagRelation>lambdaQuery()
//                        .eq(StudentTagRelation::getStuId,studentDto.getStuId()));
//                //添加新的标签
//                List<StudentTagRelation> studentTagRelations = new ArrayList<>();
//                for (Long tagId : studentDto.getTagIds()) {
//
//                }
//            }

            return Result.success(null,StudentConstants.UPDATE_SUCCESS);
        }

    }

    /**
     * 用户登录操作
     *
     * @param studentLoginDto
     * @return
     */
    @Override
    public Result studentLogin(StudentLoginDto studentLoginDto) {
        //1、检查参数
        if(StringUtils.isBlank(studentLoginDto.getStuId()) || StringUtils.isBlank(studentLoginDto.getPassword())){
            return Result.error(StudentConstants.PARAM_EMPTY);
        }
//        //1.1判断是否输入了验证码
//        if (!org.springframework.util.StringUtils.hasText(studentLoginDto.getCaptchaCode())) {
//            throw new JdzuException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND);
//        }
//        //1.2.校验验证码
//        String code01 = redisTemplate.opsForValue().get(studentLoginDto.getCaptchaKey());
//        String code = "";
//        if (code01 != null ) {
//            code01 = code01.trim();  // 修剪字符串
//            try {
////                code = Integer.parseInt(code01);
//                code = code01;
//                // 使用 code 进行后续操作
//            } catch (NumberFormatException e) {
//                // 处理解析错误
//                e.printStackTrace();
//            }
//        }
//        if (code == null) {
//            throw new JdzuException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED);
//        }
//
//        if (!code.equals(studentLoginDto.getCaptchaCode().toLowerCase())) {
//            throw new JdzuException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_ERROR);
//        }
        //2、获取用户信息
        Student student = studentMapper.selectOne(Wrappers.<Student>lambdaQuery().eq(Student::getStuId, studentLoginDto.getStuId()));
        if(student==null){
            return Result.error(StudentConstants.USER_NOT_EXIST);
        }
        //3、对密码进行MD5加密
        String password = studentLoginDto.getPassword();
        String passwordWithSalt = MD5Utils.encodeWithSalt(password, student.getSalt());
        if(!passwordWithSalt.equals(student.getPassword())){
            return Result.error(StudentConstants.PASSWORD_ERROR);
        }
        //4、开始生成jwt令牌
        Map<String,Object> claims=new HashMap<>();
        claims.put(StudentConstants.STU_ID,student.getStuId());
        String jwt = JwtUtils.createJWT(jwtProperties.getStudentSecretKey(), jwtProperties.getStudentTtl(), claims);
        Map<String,String> res=new HashMap<>();
        res.put(StudentConstants.TOKEN_NAME,jwt);
        return Result.success(jwt);
    }

    /**
     * 根据token获取学生信息
     * @param token
     * @return
     */
    @Override
    public Result getStudentInfo(String token) {
        //1、解析令牌
        try {
            Claims claims = JwtUtils.parseJWT(jwtProperties.getStudentSecretKey(), token);
            String stuId = (String) claims.get(StudentConstants.STU_ID);
            //获取学生信息
            Result result = getStudentByStuId(stuId);
            return result;
        }catch (Exception e){
            e.printStackTrace();
            return Result.error(CommonConstants.PARAM_ERROR);
        }

    }

    /**
     * 验证码
     * @return
     */
    @Override
    public CaptchaVo getCaptcha() {
        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
        specCaptcha.setCharType(Captcha.TYPE_DEFAULT);

        String code = specCaptcha.text().toLowerCase();
        String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID();
        String image = specCaptcha.toBase64();
        redisTemplate.opsForValue().set(key, code, RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);
        return new CaptchaVo(image, key);
    }

    @Override
    public void updateAvatar(String stuId, String url) {
        UpdateWrapper<Student> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("stu_id", stuId);
        Student student = new Student();
        student.setAvatar(url);
        studentMapper.update(student, updateWrapper);
    }
}

